﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace GDD2_Game3
{
    class Vehicle:GameObject
    {
        //attributes
		private Vector2 velocity;
		private Vector2 unitVelocity;

        private const float ACCELERATION_FOR_ACCELERATION_BUTTON = 300.0f;
        private const float ACCELERATION_FOR_DECCELERATION_BUTTON = -200.0f;
		private const float FRIC = 200.0f;

		private const float ACCELERATION_BY_CRASH = -1000.0f;

        private const float TURN_RIGHT = 200.0f;
        private const float TURN_LEFT = -200.0f;

		private const float SLOW_FACTOR = 0.5f;

		private const float FAKE_SPEED = 150.0f;

		public bool IsFinished { get; set; }
		public bool IsSlowed { get; set; }
		public bool IsFaster { get; set; }
		public bool TurnsFaster { get; set; }
		public bool AlwaysMoving { get; set; }
		public bool FakePhysics { get; set; }
		public bool ReversedControls { get; set; }
		public float ConstantRotation { get; set; }

		[Flags]
		public enum Keys { None = 0x0, W = 0x1, A = 0x2, S = 0x4, D = 0x8 }
		public Keys KeysHindered { get; set; }

        public Vehicle(float x, float y, float width, float height)
            :base(x, y, width, height)
        {
            velocity = new Vector2(0, 0);
			unitVelocity = new Vector2(0, 0);
			IsFinished = false;
			IsSlowed = false;
			IsFaster = false;
			TurnsFaster = false;
			AlwaysMoving = false;
			FakePhysics = false;
			ReversedControls = false;
			KeysHindered = Keys.None;
        }

		private void accelerate(float dv, float deltaTime, bool byDirection, bool byVelocity)
        {
            //Console.WriteLine(dv);
			if(IsSlowed)
			{
				dv /= 4.0f;
			}
			if(byDirection)
			{
				velocity += dv * deltaTime * direction;
			}
			if(byVelocity)
			{
				velocity += dv * deltaTime * unitVelocity;
			}
        }

        private void turn(float degreeOfTurn)
        {
            rotation += MathHelper.ToRadians(degreeOfTurn);
            direction.X = (float)Math.Cos((double)rotation);
            direction.Y = (float)Math.Sin((double)rotation);
        }

        public void move(bool isTurningLeft, bool isTurningRight, bool acceleratingFoward, bool acceleratingBackward, List<Wall> crashedInto, List<FinishLine> finish, float deltaTime)
        {
			bool leftSlow = (((KeysHindered & Keys.A) == Keys.A));
			bool rightSlow = (((KeysHindered & Keys.D) == Keys.D));
			bool upSlow = (((KeysHindered & Keys.W) == Keys.W));
			bool downSlow = (((KeysHindered & Keys.S) == Keys.S));
			acceleratingFoward |= AlwaysMoving;
			if(ReversedControls)
			{
				bool temp;
				temp = isTurningLeft;
				isTurningLeft = isTurningRight;
				isTurningRight = temp;
				temp = acceleratingFoward;
				acceleratingFoward = acceleratingBackward;
				acceleratingBackward = temp;
			}
			if(FakePhysics)
			{
				Vector2 fakeVel = Vector2.Zero;
				if(isTurningLeft)
				{
					fakeVel -= leftSlow ? Vector2.UnitX / 2.0f : Vector2.UnitX;
				}
				if(isTurningRight)
				{
					fakeVel += rightSlow ? Vector2.UnitX / 2.0f : Vector2.UnitX;
				}
				if(acceleratingFoward)
				{
					fakeVel -= upSlow ? Vector2.UnitY / 2.0f : Vector2.UnitY;
				}
				if(acceleratingBackward)
				{
					fakeVel += downSlow ? Vector2.UnitY / 2.0f : Vector2.UnitY;
				}
				fakeVel *= FAKE_SPEED * deltaTime;
				box = box.ShiftedBy(fakeVel);
				if(!(isTurningLeft && isTurningRight) && !(acceleratingFoward && acceleratingBackward))
				{
					if(isTurningLeft)
					{
						if(acceleratingFoward)
						{
							rotation = 5.0f * Convert.ToSingle(Math.PI) / 4.0f;
						}
						else if(acceleratingBackward)
						{
							rotation = 3.0f * Convert.ToSingle(Math.PI) / 4.0f;
						}
						else
						{
							rotation = Convert.ToSingle(Math.PI);
						}
					}
					else if(isTurningRight)
					{
						if(acceleratingFoward)
						{
							rotation = 7.0f * Convert.ToSingle(Math.PI) / 4.0f;
						}
						else if(acceleratingBackward)
						{
							rotation = Convert.ToSingle(Math.PI) / 4.0f;
						}
						else
						{
							rotation = 0.0f;
						}
					}
					else
					{
						if(acceleratingFoward)
						{
							rotation = 3.0f * Convert.ToSingle(Math.PI) / 2.0f;
						}
						else if(acceleratingBackward)
						{
							rotation = Convert.ToSingle(Math.PI) / 2.0f;
						}
						else
						{
						}
					}
				}
			}
			else
			{
				if(acceleratingFoward && !IsFinished && Vector2.Dot(direction, velocity) >= 0)
				{
					accelerate(ACCELERATION_FOR_ACCELERATION_BUTTON * (upSlow ? 0.25f : 1.0f) * (IsFaster ? 2.0f : 1.0f), deltaTime, true, false);
				}
				else if(acceleratingFoward && !IsFinished && Vector2.Dot(direction, velocity) < 0)
				{
					accelerate(ACCELERATION_FOR_DECCELERATION_BUTTON * (downSlow ? 0.25f : 1.0f) * (IsFaster ? 2.0f : 1.0f), deltaTime, false, true);
				}

				if(acceleratingBackward && !IsFinished && Vector2.Dot(direction, velocity) <= 0)
				{
					accelerate(ACCELERATION_FOR_DECCELERATION_BUTTON * (upSlow ? 0.25f : 1.0f) * (IsFaster ? 2.0f : 1.0f), deltaTime, true, false);
				}
				else if(acceleratingBackward && !IsFinished && Vector2.Dot(direction, velocity) > 0)
				{
					accelerate(ACCELERATION_FOR_ACCELERATION_BUTTON * (downSlow ? 0.25f : 1.0f) * (IsFaster ? 2.0f : 1.0f), deltaTime, false, true);
				}

				accelerate(-FRIC, deltaTime, false, true);
				if(!acceleratingFoward && !acceleratingBackward && !IsFinished && velocity.Length() < FRIC)
				{
					velocity = Vector2.Zero;
				}
				if(isTurningLeft && !IsFinished)
				{
					turn(TURN_LEFT * (leftSlow ? 0.25f : 1.0f) * (TurnsFaster ? 2.0f : 1.0f) * deltaTime);
				}
				if(isTurningRight && !IsFinished)
				{
					turn(TURN_RIGHT * (rightSlow ? 0.25f : 1.0f) * (TurnsFaster ? 2.0f : 1.0f) * deltaTime);
				}
				if(velocity == Vector2.Zero)
				{
					unitVelocity = Vector2.Zero;
				}
				else
				{
					unitVelocity = velocity / velocity.Length();
				}

				if(IsSlowed)
				{
					box = box.ShiftedBy(velocity * deltaTime * SLOW_FACTOR);
				}
				else
				{
					box = box.ShiftedBy(velocity * deltaTime);
				}
				IsSlowed = false;
			}
			rotation += ConstantRotation * deltaTime;
			if(IsFinished)
			{
				finishRace(deltaTime);
			}
        }

		public void teleport(Vector2 position)
		{
			box = FloatBox.CreateGivenCenter(position, Box.Dimensions);
		}

		public void collidedIntoWall(Vector2 offset, Vector2 speedFactor)
		{
			box = box.ShiftedBy(offset);
			velocity *= speedFactor;
		}

		public void finishRace(float deltaTime)
		{
			/*if(Vector2.Dot(direction, velocity) < 0)
			{
				accelerate(FRIC, deltaTime, false, true);

				if(velocity.Length() <= FRIC)
				{
					velocity = Vector2.Zero;
				}
			}

			if(Vector2.Dot(direction, velocity) > 0)
			{
				accelerate(-(FRIC), deltaTime, false, true);

				if(velocity.Length() <= FRIC)
				{
					velocity = Vector2.Zero;
				}
			}*/
			velocity = Vector2.Zero;
		}

		public void keepOnTrack(Vector2 trackDimensions)
		{
			if(Box.LeftSideX < 0.0f)
			{
				collidedIntoWall(new Vector2(-Box.LeftSideX, 0.0f), Vector2.Zero);
			}
			if(Box.RightSideX > trackDimensions.X)
			{
				collidedIntoWall(new Vector2(trackDimensions.X - Box.RightSideX, 0.0f), Vector2.Zero);
			}
			if(Box.TopSideY < 0.0f)
			{
				collidedIntoWall(new Vector2(0.0f, -Box.TopSideY), Vector2.Zero);
			}
			if(Box.BottomSideY > trackDimensions.Y)
			{
				collidedIntoWall(new Vector2(0.0f, trackDimensions.Y - Box.BottomSideY), Vector2.Zero);
			}
		}

		public void shrinkCarByHalf()
		{
			box = FloatBox.CreateGivenCenter(box.Center, box.Dimensions / 2.0f);
		}
    }
}
